//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2013 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_FUNCTORS_TRANSFORM_UNARY_H_
#define _BUOLA_FUNCTORS_TRANSFORM_UNARY_H_

#include <buola/buola.h>

namespace buola { namespace fn {

///\addtogroup functors
///@{

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// tag class for unary functions which can be used as transformations
///
/// This contains several function classes which can be used to transform a single value. Among others, it
/// contains functions to perform arithmetic operations on a value.
///
/// It also contains operators to combine the functions together.
///
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct GUnaryTransform {};
    
///\name Arithmetic unary transformations
//@{

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// add-constant transformation
///
/// Evaluated with a single argument, returns the argument plus the constant provided 
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AAddConstant : public GUnaryTransform
{
    ///initialises the comparison constant from its argument
    explicit AAddConstant(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit AAddConstant(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \p pVal+mVal
    template<typename tParam>
    tParam operator()(const tParam &pVal) const
    {
        return pVal+mVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// subtract-constant transformation
///
/// Evaluated with a single argument, returns the argument minus the constant provided 
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct ASubtractConstant : public GUnaryTransform
{
    ///initialises the comparison constant from its argument
    explicit ASubtractConstant(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit ASubtractConstant(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \p pVal-mVal
    template<typename tParam>
    tParam operator()(const tParam &pVal) const
    {
        return pVal-mVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// subtract-from-constant transformation
///
/// Evaluated with a single argument, returns the argument subtracted from the constant provided 
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct ASubtractFromConstant : public GUnaryTransform
{
    ///initialises the comparison constant from its argument
    explicit ASubtractFromConstant(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit ASubtractFromConstant(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \p mVal-pVal
    template<typename tParam>
    tParam operator()(const tParam &pVal) const
    {
        return mVal-pVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// multiply-constant transformation
///
/// Evaluated with a single argument, returns the argument multiplied by the constant provided 
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct AMultiplyConstant : public GUnaryTransform
{
    ///initialises the comparison constant from its argument
    explicit AMultiplyConstant(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit AMultiplyConstant(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \p pVal*mVal
    template<typename tParam>
    tParam operator()(const tParam &pVal) const
    {
        return pVal*mVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// divide-constant transformation
///
/// Evaluated with a single argument, returns the argument divided by the constant provided 
/// in the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct ADivideConstant : public GUnaryTransform
{
    ///initialises the comparison constant from its argument
    explicit ADivideConstant(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit ADivideConstant(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \p pVal/mVal
    template<typename tParam>
    tParam operator()(const tParam &pVal) const
    {
        return pVal/mVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// divide-from-constant transformation
///
/// Evaluated with a single argument, returns the constant provided in the constructor divided
/// by the argument
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct ADivideFromConstant : public GUnaryTransform
{
    ///initialises the comparison constant from its argument
    explicit ADivideFromConstant(const tType &pVal)
        :   mVal(pVal)
    {}
    
    ///initialises the comparison constant from its argument (move version)
    explicit ADivideFromConstant(tType &&pVal)
        :   mVal(std::move(pVal))
    {}
    
    ///returns \p mVal/pVal
    template<typename tParam>
    tParam operator()(const tParam &pVal) const
    {
        return mVal/pVal;
    }
    
private:
    tType mVal;
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// minus transformation
///
/// Evaluated with a single argument, returns minus the argument
///
/////////////////////////////////////////////////////////////////////////////////////////////////
struct AMinus : public GUnaryTransform
{
    ///returns \p -pVal
    template<typename tParam>
    tParam operator()(const tParam &pVal) const
    {
        return -pVal;
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// get Nth element from pair or tuple
///
/// Evaluated with a single argument, returns the Nth element in the argument, which must be indexable with std::get
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<size_t vIndex>
struct AGetNth : public GUnaryTransform
{
    ///returns \p -pVal
    template<typename tParam>
    auto operator()(tParam &&pVal) const -> decltype(std::get<vIndex>(std::forward<tParam>(pVal)))
    {
        return std::get<vIndex>(std::forward<tParam>(pVal));
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// concatenation
///
/// Combines two transformations, applying them in the order in which they are provided in
/// the constructor
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tLH,typename tRH>
struct AConcatenateTransform : public GUnaryTransform
{
    ///initializes the sub-transformation from the arguments
    template<typename tALH,typename tARH>
    AConcatenateTransform(tALH &&pLH,tARH &&pRH)
        :   mLH(std::forward<tALH>(pLH))
        ,   mRH(std::forward<tARH>(pRH))
    {}
    
    ///returns the result of applying \p mRH to the result of applying \p mLH to \p pVal
    template<typename tParam>
    bool operator()(const tParam &pVal) const
    {
        return mRH(mLH(pVal));
    }
    
private:
    tLH mLH;
    tRH mRH;
};

///returns a transformation which applies both transformations in order
template<typename tLH,typename tRH>
inline OEnableIfTag2<tLH,tRH,GUnaryTransform,AConcatenateTransform<ODecay<tLH>,ODecay<tRH>>>
operator>>(tLH &&pLH,tRH &&pRH)
{
    return {std::forward<tLH>(pLH),std::forward<tRH>(pRH)};
}

///returns a transformation which returns the result of adding \p pVal to its argument
template<typename tType>
inline AAddConstant<ODecay<tType>> add(tType &&pVal)
{
    return AAddConstant<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a transformation which returns the result of subtracting \p pVal from its argument
template<typename tType>
inline ASubtractConstant<ODecay<tType>> subtract(tType &&pVal)
{
    return ASubtractConstant<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a transformation which returns the result of subtracting its argument from \p pVal
template<typename tType>
inline ASubtractFromConstant<ODecay<tType>> subtract_from(tType &&pVal)
{
    return ASubtractFromConstant<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a transformation which returns the result of multiplying its argument by \p pVal
template<typename tType>
inline AMultiplyConstant<ODecay<tType>> multiply(tType &&pVal)
{
    return AMultiplyConstant<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a transformation which returns the result of dividing its argument by \p pVal
template<typename tType>
inline ADivideConstant<ODecay<tType>> divide(tType &&pVal)
{
    return ADivideConstant<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a transformation which returns the result of dividing \p pVal by its argument
template<typename tType>
inline ADivideFromConstant<ODecay<tType>> divide_from(tType &&pVal)
{
    return ADivideFromConstant<ODecay<tType>>(std::forward<tType>(pVal));
}

///returns a transformation which returns the key of an associative container element
inline AGetNth<0> keys()
{
    return {};
}

///returns a transformation which returns the values of an associative container element
inline AGetNth<1> values()
{
    return {};
}

//@}

///\name Miscellaneous transformations
//@{

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///
/// indirection functor
///
/// Its `operator()` returns the result of dereferencing its argument
///
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct ADereference : public GUnaryTransform
{
    ///returns the result of dereferencing \p pV
    template<typename tType>
    auto operator()(tType &&pV) const -> decltype(*pV)
    {
        return *pV;
    }
};

/////////////////////////////////////////////////////////////////////////////////////////////////
///
/// cast element to new type
///
/// Evaluated with a single argument, returns the argument cast to \p tType
///
/////////////////////////////////////////////////////////////////////////////////////////////////
template<typename tType>
struct ACast : public GUnaryTransform
{
    ///returns \p tType(\p pVal)
    template<typename tParam>
    tType operator()(tParam &&pVal) const
    {
        return tType(std::forward<tType>(pVal));
    }
};

//@}

///@}

/*namespace fn*/ } /*namespace buola*/ }

#endif
